package com.rdzn.mall.portal.config.shiro;

import com.rdzn.mall.portal.domain.MemberDetails;
import com.rdzn.mall.portal.service.IUserService;
import com.rdzn.mall.portal.util.JwtTokenUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.oConvertUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;



/**
 * @Description: 用户登录鉴权和获取用户授权
 * @Author: Scott
 * @Date: 2019-4-23 8:13
 * @Version: 1.1
 */
@Component
@Slf4j
public class ShiroRealm extends AuthorizingRealm {

	@Autowired
	private IUserService userService;
	@Autowired
	private JwtTokenUtil jwtTokenUtil;


	/**
	 * 注意坑点 : 必须重写此方法，不然Shiro会报错
	 * 因为创建了 JWTToken 用于替换Shiro原生 token,所以必须在此方法中显式的进行替换，否则在进行判断时会一直失败
	 */
	@Override
	public boolean supports(AuthenticationToken token) {
		return token instanceof JwtToken;
	}

	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		return new SimpleAuthorizationInfo();
	}

	/**
	 * 校验 验证token逻辑
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) {
		if (auth instanceof UsernamePasswordToken){
			UsernamePasswordToken token = (UsernamePasswordToken) auth;
			MemberDetails memberDetails = userService.getUserById(Integer.parseInt(token.getUsername()));
			if (memberDetails.getUmsMember() == null) {
				throw new AuthenticationException("用户不存在!");
			}
			return new SimpleAuthenticationInfo(memberDetails, token, getName());
		}else {
			String token = (String) auth.getCredentials();
			if (token == null) {
				log.info("————————身份认证失败——————————IP地址:  "+ oConvertUtils.getIpAddrByRequest(SpringContextUtils.getHttpServletRequest()));
				throw new AuthenticationException("token为空!");
			}
			// 校验token有效性
			MemberDetails memberDetails = this.checkUserTokenIsEffect(token);

			return new SimpleAuthenticationInfo(memberDetails, token, getName());
		}
	}

	/**
	 * 校验token的有效性
	 *
	 * @param token
	 */
	public MemberDetails checkUserTokenIsEffect(String token) throws AuthenticationException {
		// 解密获得username，用于和数据库进行对比
		String username = jwtTokenUtil.getUserNameFromToken(token);
		if (username == null) {
			throw new AuthenticationException("token非法无效!");
		}

		// 查询用户信息
		log.debug("———校验token是否有效————checkUserTokenIsEffect——————— "+ token);
		MemberDetails userDetails = userService.getUserDetails(username);
		if (userDetails.getUmsMember() == null) {
			throw new AuthenticationException("用户不存在!");
		}
		// 判断用户状态
//		if (userDetails.getStatus() != 1) {
//			throw new AuthenticationException("账号已被锁定,请联系管理员!");
//		}
		// 校验token是否超时失效 & 或者账号密码是否错误
//		if (!jwtTokenRefresh(token, userDetails)) {
//			throw new AuthenticationException("Token失效，请重新登录!");
//		}
		if (jwtTokenUtil.validateToken(token, userDetails)) {
			UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
//			authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
			log.debug("authenticated user:{}", username);
			SecurityContextHolder.getContext().setAuthentication(authentication);
//			UsernamePasswordToken token1 = new UsernamePasswordToken(userDetails.getUmsMember().getId()+"", userDetails.getUmsMember().getWeixinOpenid());
//			SecurityUtils.getSubject().login(token1);
		}
		return userDetails;
	}

	/**
	 * JWTToken刷新生命周期 （实现： 用户在线操作不掉线功能）
	 * 1、登录成功后将用户的JWT生成的Token作为k、v存储到cache缓存里面(这时候k、v值一样)，缓存有效期设置为Jwt有效时间的2倍
	 * 2、当该用户再次请求时，通过JWTFilter层层校验之后会进入到doGetAuthenticationInfo进行身份验证
	 * 3、当该用户这次请求jwt生成的token值已经超时，但该token对应cache中的k还是存在，则表示该用户一直在操作只是JWT的token失效了，程序会给token对应的k映射的v值重新生成JWTToken并覆盖v值，该缓存生命周期重新计算
	 * 4、当该用户这次请求jwt在生成的token值已经超时，并在cache中不存在对应的k，则表示该用户账户空闲超时，返回用户信息已失效，请重新登录。
	 * 注意： 前端请求Header中设置Authorization保持不变，校验有效性以缓存中的token为准。
	 *       用户过期时间 = Jwt有效时间 * 2。
	 *
	 * @param userDetails
	 * @return
	 */
//	public boolean jwtTokenRefresh(String token, UserDetails userDetails) {
//		String cacheToken = String.valueOf(redisUtil.get(CommonConstant.PREFIX_USER_TOKEN + token));
//		if (oConvertUtils.isNotEmpty(cacheToken)) {
//			// 校验token有效性
//			if (!jwtTokenUtil.validateToken(cacheToken, userDetails)) {
//				String newAuthorization = jwtTokenUtil.refreshHeadToken(cacheToken);
//				// 设置超时时间
//				redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, newAuthorization);
//				redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, jwtTokenUtil.getExpiration() *2 / 1000);
//				log.info("——————————用户在线操作，更新token保证不掉线—————————jwtTokenRefresh——————— "+ token);
//			}
//			//update-begin--Author:scott  Date:20191005  for：解决每次请求，都重写redis中 token缓存问题
////			else {
////				// 设置超时时间
////				redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, cacheToken);
////				redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME / 1000);
////			}
//			//update-end--Author:scott  Date:20191005   for：解决每次请求，都重写redis中 token缓存问题
//			return true;
//		}
//		return false;
//	}

}
